package net.jueb.protoServer.boot;
import java.util.Iterator;
import net.jueb.protoServer.handler.TcpServicePortInitializer;
import net.jueb.protoServer.handler.UdpServicePortInitializer;
import net.jueb.protoServer.portService.PortService;
import net.jueb.protoServer.portService.PortService.PortType;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

public class ProtoServer extends AbstractServer{
	
	protected static final InternalLogger log = InternalLoggerFactory.getInstance(ProtoServer.class);
	
	private final String serverIp="0.0.0.0";
	private EventLoopGroup bossGroup;
	private EventLoopGroup workerGroup;
	private ServerBootstrap tcpServer;
	
	private EventLoopGroup udpbGroup;
	private Bootstrap udpServer;
		
	public ProtoServer(int serverId,String serverName)
	{
		super(serverId,serverName);
	}
		
	/**
	 * 查看TCP模式是否准备就绪
	 * @return
	 */
	private boolean tcpIsReady()
	{
		return bossGroup!=null && workerGroup!=null && tcpServer!=null && !bossGroup.isShutdown() && !workerGroup.isShutdown();
	}
	/**
	 * 查看UDP模式是否准备就绪
	 * @return
	 */
	private boolean udpIsReady()
	{
		return udpbGroup!=null && !udpbGroup.isShutdown() && udpServer!=null;
	}
	@Override
	public final synchronized void startServer() {
		startServerBefore();
		initTcp();
		initUdp();
		startServerAfter();
		log.info("服务器启动完成!");
	}
	protected  void startServerBefore(){}
	protected  void startServerAfter(){}
	protected  void shutdownBefore(){}
	protected  void shutdownAfter(){}
	@Override
	public final synchronized void shutdown() {
		shutdownBefore();
		//遍历所有服务并且关闭
		Iterator<PortService<?>> it=portServices.values().iterator();
		while(it.hasNext())
		{
			closePort(it.next());
		}
		log.info("开始关闭服务器!");
		if(bossGroup!=null && !bossGroup.isShutdown())
		{
			bossGroup.shutdownGracefully();
			bossGroup=null;
		}
		if(workerGroup!=null && !workerGroup.isShutdown())
		{
			workerGroup.shutdownGracefully();
			workerGroup=null;
		}
		tcpServer=null;
		if(udpbGroup!=null && !udpbGroup.isShutdown())
		{
			udpbGroup.shutdownGracefully();
			udpbGroup=null;
		}
		udpServer=null;
		shutdownAfter();
		log.info("服务器关闭!");
	}

	/**
	 * 初始化TCP服务器
	 */
	private void initTcp() {
		bossGroup = new NioEventLoopGroup();//主线程池
	    workerGroup = new NioEventLoopGroup();//从线程池
	    tcpServer = new ServerBootstrap();
	    tcpServer.group(bossGroup, workerGroup);
	    tcpServer.channel(NioServerSocketChannel.class);
	    tcpServer.option(ChannelOption.SO_BACKLOG, 1024);//设置连接等待最大队列
		tcpServer.option(ChannelOption.TCP_NODELAY,true);
	    tcpServer.option(ChannelOption.SO_KEEPALIVE, true);//设置保持连接
	    //添加管道初始化handler,该handler只创建一次,以后每次请求都是调用该对象方法
	    tcpServer.handler(new LoggingHandler());//添加日志
	    tcpServer.childHandler(new TcpServicePortInitializer(portServices));
		log.info("TCP服务初始化完成!");
	}
	
	/**
	 * 初始化UDP服务器
	 */
	private void initUdp()
	{
		udpbGroup=new NioEventLoopGroup();
		udpServer=new Bootstrap();
		udpServer.group(udpbGroup);
		udpServer.channel(NioDatagramChannel.class);
		//SO_BROADCAST 允许广播地址发送和接收信息包。只对UDP有效
		udpServer.option(ChannelOption.SO_BROADCAST, true);//广播支持
		udpServer.handler(new UdpServicePortInitializer(portServices));
		log.info("UDP服务初始化完成!");
	}

	/**
	 * 负责开启端口
	 * 绑定通道到服务
	 * 如果出现异常则解绑,但是保留服务
	 * @param portService
	 * @return
	 */
	private boolean openPort(final PortService<?> portService)
	{
		try {
			final int port=portService.getPort();
			//开启端口
        	if(PortType.TCP==portService.getPortType())
        	{//开启tcp端口通道
        		if(!tcpIsReady())
        		{
        			log.error("服务器未启动!");
        			return false;
        		}
        		final Channel channel=tcpServer.bind(serverIp, port).sync().channel();
        		//绑定服务
            	portService.bindChannel(channel);
            	log.info("端口:"+port+"开启!");
            	//开启线程阻塞
            	new Thread("WaiteClosePort:"+port){
    	        	@Override
    				public void run() {
    	        		try {
    	        			channel.closeFuture().sync();
    	        			/**
    	        			 * 如果是其它异常触发的端口关闭,则手动删除和解绑
    	        			 */
    	        			portService.unBindChannel();
    	                	log.info("端口:"+port+"关闭!");
    	        		} catch (InterruptedException e) {
    	        			e.printStackTrace();
    	        		} 
    	        	};
    	        }.start();
        	}else if(PortType.UDP==portService.getPortType())
        	{//开启udp端口通道
        		if(!udpIsReady())
        		{
        			log.error("服务器未启动!");
        			return false;
        		}
        		final Channel channel=udpServer.bind(serverIp, port).sync().channel();
        		//绑定服务
            	portService.bindChannel(channel);
            	log.info("端口:"+port+"开启!");
            	//开启线程阻塞
            	new Thread("WaiteClosePort:"+port){
    	        	@Override
    				public void run() {
    	        		try {
    	        			channel.closeFuture().sync();
    	        			/**
    	        			 * 如果是其它异常触发的端口关闭,则手动删除和解绑
    	        			 */
    	        			portService.unBindChannel();
    	                	log.info("端口:"+port+"关闭!");
    	        		} catch (InterruptedException e) {
    	        			e.printStackTrace();
    	        		} 
    	        	};
    	        }.start();
        	}
		} catch (InterruptedException e) {
			//如果出现异常则尝试关闭管道,解绑管道,删除记录
			closePort(portService);
			return false;
		}
        return true;
	}
	
	
	/**
	 * 尝试关闭管道
	 * 解绑端口服务的netty通道
	 * @param port
	 * @return
	 */
	private  boolean closePort(PortService<?> portService)
	{
		log.info("开始停止端口服务!");
		Channel channel=portService.getBindChannel();
		if(channel!=null)
		{//尝试关闭管道
			channel.close();
			if(channel!=null && (channel.isActive()||channel.isActive()))
			 {
				 return false;
			 }
		}
		portService.unBindChannel();
		log.info("端口服务:"+portService.getPortServiceName()+"停止!");
		return true;
	}
	
	@Override
	public final synchronized boolean addPortService(PortService<?> portService) {
		int port=portService.getPort();
		//先查找是否存在该端口管道
		if(portServices.containsKey(port))
		{
			log.error("该端口"+port+"服务已经运行!");
			return false;
		}else
		{//否则则启动该服务
			if(openPort(portService))
			{//启动成功则添加
				portServices.put(port,portService);
				return true;
			}
			return false;
		}
	}

	@Override
	public final synchronized boolean removePortService(int port) {
		//先查找是否存在该端口管道
		if(portServices.containsKey(port))
		{
			if(closePort(portServices.get(port)))
			{
				portServices.remove(port);
				return true;
			}else
			{
				log.error("端口"+port+"服务停止失败!");
				return false;
			}
		}
		return true;
	}
}
